//
//  PSLinkedList.m
//  PrefCore
//
//  Created by Sergey on 5/6/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import "PSLinkedList.h"

@interface PSLinkedList () {

    id<PSListEntry> _head;

    id<PSListEntry> _tail;

    NSUInteger _count;

    __weak id<PSListEntry> _iterator;

}

@end

@interface PSLinkedListAnchor : NSObject <PSListEntry>

@property (nonatomic, readwrite) id <PSListEntry> previous;

@property (nonatomic, readwrite) id <PSListEntry> next;

@end

@implementation PSLinkedListAnchor

-(instancetype)init {
    if (self = [super init]) {
        _previous = nil;
        _next = nil;
    }
    return self;
}

@end

@implementation PSLinkedList

-(instancetype)init {
    if (self = [super init]) {
        _head = [[PSLinkedListAnchor alloc] init];
        _tail = [[PSLinkedListAnchor alloc] init];
        _head.next = _tail;
        _tail.previous = _head;
        _iterator = _tail;
        _count = 0;
    }
    return self;
}

-(void)clear {
    id<PSListEntry> current = _head.next;
    while (current != _tail) {
        id<PSListEntry> tmp = current;
        current = current.next;
        tmp.previous = tmp.next = nil;
    }
    _head.next = _tail;
    _tail.previous = _head;
    _iterator = _tail;
    _count = 0;
}

-(void)dealloc {
    id<PSListEntry> current = _head.next;
    while (current != _tail) {
        id<PSListEntry> tmp = current;
        current = current.next;
        tmp.previous = tmp.next = nil;
    }
    _head.next = _tail.previous = _iterator = nil;
}

-(void)addFirst:(id<PSListEntry>) entry {
    NSAssert(entry, @"Adding nil entries is not allowed.");
    entry.next = _head.next;
    _head.next.previous = entry;
    _head.next = entry;
    entry.previous = _head;
    _count++;
}

-(void)addLast:(id<PSListEntry>) entry {
    NSAssert(entry, @"Adding nil entries is not allowed.");
    entry.previous = _tail.previous;
    _tail.previous.next = entry;
    _tail.previous = entry;
    entry.next = _tail;
    _count++;
}

-(void)moveAppend:(PSLinkedList *)list {
    list->_head.next.previous = _tail.previous;
    _tail.previous.next = list->_head.next;
    _tail.previous = list->_tail.previous;
    list->_tail.previous.next = _tail;
    list->_head.next = list->_tail;
    list->_tail.previous = list->_head;
    _count += list->_count;
    list->_count = 0;
}

-(id<PSListEntry>)removeFirst {
    id<PSListEntry> res = _head.next;
    NSAssert(res != _tail, @"Called removeFist on an empty list.");
    _head.next = res.next;
    res.next.previous = _head;
    _count--;
    res.previous = res.next = nil;
    return res;
}

-(id<PSListEntry>)removeLast {
    id<PSListEntry> res = _tail.previous;
    NSAssert(res != _head, @"Called removeLast on an empty list.");
    _tail.previous = res.previous;
    res.previous.next = _tail;
    res.next = res.previous = nil;
    _count--;
    return res;
}

-(void)remove:(id<PSListEntry>)entry {
    if (entry == _iterator) {
        _iterator = _iterator.previous;
    }
    entry.previous.next = entry.next;
    entry.next.previous = entry.previous;
    entry.next = entry.previous = nil;
    _count--;
}

-(id<PSListEntry>)first {
    return _head.next;
}

-(void)start {
    _iterator = _head.next;
}

-(BOOL)hasNextEntry {
    return _iterator != _tail;
}

-(id<PSListEntry>)nextEntry {
    NSAssert(_iterator != _tail, @"Calling nextEntry in the final position.");
    id<PSListEntry> res = _iterator;
    _iterator = _iterator.next;
    return res;
}

-(NSUInteger) count {
    return _count;
}

-(void)removeCurrentEntry {
    id<PSListEntry> current = _iterator.previous;
    NSAssert(current != _head, @"Calling removeCurrentEntry on an empty list.");
    current.previous.next = current.next;
    current.next.previous = current.previous;
    _iterator = current.next;
    current.next = current.previous = nil;
    _count--;
}

-(NSString*)description {
    NSMutableString *res = [NSMutableString string];
    [res appendString:@"{"];
    id<PSListEntry> current = _head.next;
    NSString *format = @"%@";
    while (current != _tail) {
        [res appendFormat:format, [current description]];
        format = @", %@";
        current = current.next;
    }
    [res appendString:@"}"];
    return [res copy];
}

@end
